<#assign class = bali[0].class>
<#assign annotation = bali[0].annotation>
<#assign validations = (bali[0].Validation)![]>
package ${class.enclosingElement.qualifiedName};

import java.util.Collection;
import javax.inject.Singleton;
import javax.inject.Inject;
import java.util.ArrayList;

/**
 * Trivial base class to allow specifying a validation for just one method of an interface.
 *
 * Contains static inner classes to decorate an instance to run validations before real methods.
 *
 * Subclasses are not thread safe.
 */
public class ${class.simpleName}Validation <#if class.superclass.toString() != "java.lang.Object">extends ${class.superclass}Validation </#if>implements ${annotation.validatedInterface.qualifiedName} {
<#if class.superclass.toString() == "java.lang.Object">	protected ${class.qualifiedName} entity;
</#if>
	public ${annotation.validationResultClass.qualifiedName} validationResult;

	public void setEntity(${class.qualifiedName} entity) {
		this.entity = entity;
		validationResult = null;
	}

	protected ${class.qualifiedName} getEntity() {
		return (${class.qualifiedName})entity;
	}

<#list class.enclosedElements as x>
<#if x.kind.toString() == "METHOD">

	public ${x.returnType} ${x.simpleName}(<#list x.parameters as p>${p.asType()} ${p}<#if p_has_next>, </#if></#list>) {
		<#if x.returnType != "void">
<#if x.returnType.kind.toString() == "BOOLEAN">			return false;
<#elseif x.returnType.class.interfaces?seq_contains("interface javax.lang.model.type.PrimitiveType")>			return 0;
<#else>			return null;
</#if>
		</#if>
	}
</#if>
</#list>

	@Singleton
	public static class DecoratorFactory <#if class.superclass.toString() != "java.lang.Object">extends ${class.superclass}Validation.DecoratorFactory </#if>{
<#list validations as v>
		@Inject
		private ${v.qualifiedName} ${v.simpleName?uncap_first};
</#list>

		private Collection<${class.simpleName}Validation> validations = new ArrayList();
<#if class.superclass.toString() == "java.lang.Object">

		protected Collection<${annotation.validationResultClass.qualifiedName}> validationResults;
</#if>

		public DecoratorFactory() {
<#list validations as v>
			validations.add(${v.simpleName?uncap_first});
</#list>
		}

		public ${annotation.validatedInterface.qualifiedName} create(${class.qualifiedName} entity) {
			validationResults = new ArrayList<${annotation.validationResultClass.qualifiedName}>();
			Decorator decorator = new Decorator(entity, validationResults);
			setValidations(decorator);
			return decorator;
		}

		protected void setValidations(Decorator decorator) {
<#if class.superclass.toString() != "java.lang.Object">
			super.setValidations(decorator);
</#if>
			decorator.set${class.simpleName?cap_first}Validations(validations);
		}
	
		public Collection<${annotation.validationResultClass.qualifiedName}> getViolations() {
			return validationResults;
		}
	}

	//TODO should this just be an inner class of the Factory?
	protected static class Decorator <#if class.superclass.toString() != "java.lang.Object">extends ${class.superclass}Validation.Decorator </#if>implements ${annotation.validatedInterface.qualifiedName} {
		private Collection<${class.simpleName}Validation> validations;

<#if class.superclass.toString() == "java.lang.Object">
		protected ${class.qualifiedName} entity;
		protected Collection<${annotation.validationResultClass.qualifiedName}> validationResults;

</#if>
		protected Decorator(${class.qualifiedName} entity, Collection<${annotation.validationResultClass.qualifiedName}> validationResults) {
<#if class.superclass.toString() == "java.lang.Object">			this.entity = entity;
			this.validations = validations;
			this.validationResults = validationResults;
<#else>
			super(entity, validationResults);
</#if>
		}

		protected void set${class.simpleName?cap_first}Validations(Collection<${class.simpleName}Validation> validations) {
			this.validations = validations;
		}
<#assign c=class>
<#list 1..20 as i>
<#list c.enclosedElements![] as m><#if m.kind.toString() == "METHOD">

		public ${m.returnType} ${m.simpleName}(<#list m.parameters as p>${p.asType()} ${p}<#if p_has_next>, </#if></#list>) {
			for (${class.simpleName}Validation validation : validations) {
				validation.setEntity((${class.qualifiedName})entity);
				validation.${m.simpleName}(<#list m.parameters as p>${p}<#if p_has_next>, </#if></#list>);
				validationResults.add(validation.validationResult);
				validation.setEntity(null);
			}

<#if c == class>
			if (validationResults.isEmpty()) {
				<#if m.returnType != "void">return </#if>((${class.qualifiedName})entity).${m.simpleName}(<#list m.parameters as p>${p}<#if p_has_next>, </#if></#list>);
			}<#if m.returnType != "void"> else {
<#if m.returnType.kind.toString() == "BOOLEAN">				return false;
<#elseif m.returnType.class.interfaces?seq_contains("interface javax.lang.model.type.PrimitiveType")>				return 0;
<#else>				return null;
</#if>
			}</#if>
<#else>
			<#if m.returnType != "void">return </#if>super.${m.simpleName}(<#list m.parameters as p>${p}<#if p_has_next>, </#if></#list>);
</#if>
		}
</#if></#list>
<#if c.superclass.toString() == "java.lang.Object">
<#break>
</#if>
<#assign c=c.superclass.asElement()>
</#list>
	}
}
